package com.fpost.dao.impl;

import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

import com.fpost.dao.GenericDAO;
import com.fpost.domain.Article;
import com.fpost.domain.ArticleDictionary;
import com.fpost.domain.ArticleVersion;
import com.fpost.domain.Dictionary;
import com.fpost.manager.ArticleManager;
import com.fpost.regex.util.RegexUtils;

public class DictionaryDAO extends GenericDAO<Dictionary> {
	
	@Override
	public Long save(Dictionary entry) throws HibernateException {
		Long entryId = null;
		Session session = getSessionFactory().openSession();
		try {
			session.beginTransaction();
			entryId = (Long) session.save(entry);
			session.getTransaction().commit();
		} catch (HibernateException e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
		return entryId;
	}
	
	public void taskPutArticlesInDictionary() {
 
		List<Long> articleIds = new ArticleDAO().getCurrentArticleIdsByInDictionaryFlag(false);		
		//System.out.print("ArticleDictionaryTask ");
		if (articleIds.size() == 0) {			
			//System.out.println("found no articles to process at " + new Date());
		} else {
			//System.out.println("found " + articleIds.size() + " article(s) to process at " + new Date());
			for (Long articleId : articleIds) {
				addNewWords(articleId);
			}
		}
	}
	
	public void addNewWords(final long articleId) throws HibernateException {

		//need to do it in thread so that user does not have to wait until
		//records added to DICTIONARY and/or ARTICLE_DICTIONARY tables
		Runnable r  = new Runnable(){
			public void run() {				
				saveNewWords(articleId);
			}};
		Thread thread = new Thread(r);
		//thread.setDaemon(true);
		thread.start();		
	}

	/**
	 * it takes time to process large number of words (approx 25 sec for 2500 words)
	 * @param article
	 * @return job status (true if done w/o throwing an exception)
	 * @throws HibernateException
	 */
	private synchronized void saveNewWords(long articleId) throws HibernateException {
	
		Session session = null;
		Transaction tx = null;
		Article article = null;
		try {
			long startTime = System.currentTimeMillis();
			article = new ArticleManager().getArticleById(articleId);			
			ArticleVersion lastVersion = article.getVersions().get(0);
			
			StringBuilder sb = new StringBuilder();
			sb.append(lastVersion.getTitle().concat(" "));
			sb.append(lastVersion.getSummary().concat(" "));
			sb.append(lastVersion.getBody().concat(" "));
			sb.append(lastVersion.getKeywords());
			
			Set<String> set = RegexUtils.cleanArticleContentUTF(sb.toString());
			if (set.isEmpty()) return;
			
			session = getSessionFactory().openSession();
			tx = session.beginTransaction();				
			String temp = null;
			for (Object object : set) {			
				temp = (String)object;
				if (temp.length() != 0) {					
					Dictionary dictionary = (Dictionary)session.createCriteria(Dictionary.class)
						.add(Restrictions.eq("entry", temp)).uniqueResult();
					if (dictionary == null) {												
						Long dictionaryId = (Long)session.save(new Dictionary(temp));
						session.save(new ArticleDictionary(article.getArticleId(), dictionaryId));						
					} else {
						ArticleDictionary  articleDictionary = (ArticleDictionary)session.createCriteria(ArticleDictionary.class)
								.add(Restrictions.eq("articleId", article.getArticleId()))
								.add(Restrictions.eq("dictionaryId", dictionary.getDictionaryId()))
								.uniqueResult();
						if (articleDictionary == null) {
							session.save(new ArticleDictionary(article.getArticleId(), dictionary.getDictionaryId()));
						}
					}
				}
			}			
			article.setInDictionary(true);
			session.saveOrUpdate(article);
			tx.commit();
			long time = System.currentTimeMillis() - startTime;
			System.out.println("article(" + article.getArticleId() + 
					") has been processed in " + time/1000 + " sec at " + new Date());
			
		} catch (org.hibernate.exception.ConstraintViolationException e) {
			e.printStackTrace();
			System.out.println("" + e.getConstraintName() + " was thrown when processing article(" + article.getArticleId() + ") at " + new Date());
		} catch (HibernateException e) {			
			if (tx != null) tx.rollback();
			throw e;
		} finally {
			if (session.isOpen()) session.close();
		}
	}

	@Override
	public void delete(long id) throws HibernateException {
		Session session = getSessionFactory().openSession();
		try {
			session.beginTransaction();
			session.delete(get(id));
			session.getTransaction().commit();
		} catch (HibernateException e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}

	}

	@Override
	public void delete(Dictionary entry) throws HibernateException {
		Session session = getSessionFactory().openSession();
		try {
			session.beginTransaction();
			session.delete(entry);
			session.getTransaction().commit();
		} catch (HibernateException e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}

	}

	@Override
	public Dictionary get(long id) throws HibernateException {
		Session session = getSessionFactory().openSession();
		Dictionary Entry = null;
		try {
			session.beginTransaction();
			Entry = (Dictionary) session.get(Dictionary.class, id);
			session.getTransaction().commit();
		} catch (HibernateException e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
		return Entry;
	}
	
	public Dictionary getByEntry(String word) throws HibernateException {
		Session session = getSessionFactory().openSession();
		Dictionary entry = (Dictionary) session.createCriteria(Dictionary.class)
				.add(Restrictions.eq("entry", word)).uniqueResult();

		return entry;
	}


	@Override
	public void saveOrUpdate(Dictionary entry) throws HibernateException {
		Session session = getSessionFactory().openSession();
		try {
			session.beginTransaction();
			session.saveOrUpdate(entry);
			session.getTransaction().commit();
		} catch (HibernateException e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}

	}
	
	// search related method
	public List<Dictionary> getDictionaryList(List<String> articleEntries) {
				
		Session session = getSessionFactory().openSession();
		Criteria crit = session.createCriteria(Dictionary.class);
		crit.add(Restrictions.in("entry", articleEntries));
		List<Dictionary> dictionaryList = null;
		try {
			dictionaryList = (List<Dictionary>)crit.list();
		} catch (HibernateException e) {
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
		return dictionaryList;
	}
	
	public List<Dictionary> getAll() throws HibernateException {
		Session session = getSessionFactory().openSession();
		List<Dictionary> dictionaryList;
		try {
			dictionaryList = (List<Dictionary>) session.createCriteria(Dictionary.class).list();
		} catch (HibernateException e) {
			e.printStackTrace();
			throw e;
		}
		return dictionaryList;		
	}
}
